#include "neighborhood.h"
#include "freefuncs.h"
#include <assert.h>

int Neighborhood::getFeature (int i) const {return this->feature[i];}

int Neighborhood::getHash (void) const {return this->hash;}

void Neighborhood::setFeature (int pos, int val) 
{
	if (pos < NEIGHBORHOODSIZE1)
		this->feature[pos] = val;
	this->updateHash();
}

void Neighborhood::setFeatureLeaveHash(int pos, int val) 
{
	if (pos < NEIGHBORHOODSIZE1)
		this->feature[pos] = val;
}

bool Neighborhood::hasSameFeature(const Neighborhood& other) const
{
	for (int i = 0; i != NEIGHBORHOODSIZE1; i++)
		if (this->feature[i] != other.feature[i])
			return false;
	return true;
}

void Neighborhood::updateFromHash (unsigned long hashValue)
{
	unsigned long dynamicNum = hashValue;
	this->hash = hashValue;
	for (int bitIndex = 0; bitIndex != NEIGHBORHOODSIZE1-1 ; bitIndex++)
	{
		this->feature[bitIndex] = dynamicNum % CELLSTATES;
		dynamicNum = dynamicNum / CELLSTATES;
	}
	this->feature[NEIGHBORHOODSIZE1-1] = dynamicNum;
}

void Neighborhood::updateFromImageMatrix(const ImageMatrix& image, int row, int col)
{
	this->feature[0] = image.getValue(row-1,col-1);
	this->feature[1] = image.getValue(row-1,col);
	this->feature[2] = image.getValue(row-1,col+1);
	this->feature[3] = image.getValue(row,col-1);
	this->feature[4] = image.getValue(row,col);
	this->feature[5] = image.getValue(row,col+1);
	this->feature[6] = image.getValue(row+1,col-1);
	this->feature[7] = image.getValue(row+1,col);
	this->feature[8] = image.getValue(row+1,col+1);
}

//void Neighborhood::updateFromImageMatrix(const ImageMatrix& image, int row, int col)
//{
//	assert(col < image.getColNum() && row < image.getRowNum());
//	this->feature[0] = image.getValue(row,col);
//	switch (row)
//	{
//	case 0:
//		this->feature[1] = OUTOFBOARD;
//		this->feature[2] = OUTOFBOARD;
//		this->feature[3] = OUTOFBOARD;
//
//		switch (col)
//		{
//		case 0:
//			this->feature[4] = image.getValue(row, col+1);
//			this->feature[5] = image.getValue(row+1, col+1);
//			this->feature[6] = image.getValue(row+1, col);
//			this->feature[7] = OUTOFBOARD;
//			this->feature[8] = OUTOFBOARD;
//			break;
//		case MCOLS-1:
//			this->feature[4] = OUTOFBOARD;
//			this->feature[5] = OUTOFBOARD;
//			this->feature[6] = image.getValue(row+1, col);
//			this->feature[7] = image.getValue(row+1, col-1);
//			this->feature[8] = image.getValue(row, col-1);
//		break;
//		default:
//			this->feature[4] = image.getValue(row, col+1);
//			this->feature[5] = image.getValue(row+1, col+1);
//			this->feature[6] = image.getValue(row+1, col);
//			this->feature[7] = image.getValue(row+1, col-1);
//			this->feature[8] = image.getValue(row, col-1);
//		}
//		break;
//
//
//	case MROWS-1:
//		this->feature[5] = OUTOFBOARD;
//		this->feature[6] = OUTOFBOARD;
//		this->feature[7] = OUTOFBOARD;
//		switch (col)
//		{
//		case 0:
//			this->feature[1] = OUTOFBOARD;
//			this->feature[2] = image.getValue(row-1, col);
//			this->feature[3] = image.getValue(row-1, col+1);
//			this->feature[4] = image.getValue(row, col+1);
//			this->feature[8] = OUTOFBOARD;
//			break;
//		case MCOLS-1:
//			this->feature[1] = image.getValue(row-1, col-1);
//			this->feature[2] = image.getValue(row-1, col);
//			this->feature[3] = OUTOFBOARD;
//			this->feature[4] = OUTOFBOARD;
//			this->feature[8] = image.getValue(row, col-1);
//			break;
//		default:
//			this->feature[1] = image.getValue(row-1, col-1);
//			this->feature[2] = image.getValue(row-1, col);
//			this->feature[3] = image.getValue(row-1, col+1);
//			this->feature[4] = image.getValue(row, col+1);
//			this->feature[8] = image.getValue(row, col-1);
//		}
//		break;
//
//	default:
//		this->feature[2] = image.getValue(row-1, col);
//		this->feature[6] = image.getValue(row+1, col);
//		switch (col)
//		{
//		case 0:
//			this->feature[1] = OUTOFBOARD;
//			this->feature[7] = OUTOFBOARD;
//			this->feature[8] = OUTOFBOARD;
//			this->feature[3] = image.getValue(row-1, col+1);
//			this->feature[4] = image.getValue(row, col+1);
//			this->feature[5] = image.getValue(row+1, col+1);
//			break;
//		case MCOLS-1:
//			this->feature[3] = OUTOFBOARD;
//			this->feature[4] = OUTOFBOARD;
//			this->feature[5] = OUTOFBOARD;
//			this->feature[1] = image.getValue(row-1, col-1);
//			this->feature[7] = image.getValue(row, col-1);
//			this->feature[8] = image.getValue(row+1, col-1);
//			break;
//		default:
//			this->feature[1] = image.getValue(row-1, col-1);
//			this->feature[7] = image.getValue(row, col-1);
//			this->feature[8] = image.getValue(row+1, col-1);
//			this->feature[3] = image.getValue(row-1, col+1);
//			this->feature[4] = image.getValue(row, col+1);
//			this->feature[5] = image.getValue(row+1, col+1);
//		}
//	}
//	this->updateHash();
//}

void Neighborhood::updateHash(void)
{
	unsigned long result = 0;
	for (int i = 0; i != NEIGHBORHOODSIZE1; i++)
		result += this->feature[i] * intPosPow(CELLSTATES, i);
	this->hash = result;
}

Neighborhood::Neighborhood(unsigned long hashValue)
{
	this->feature = new int[NEIGHBORHOODSIZE1];
	this->updateFromHash(hashValue);
}

Neighborhood::Neighborhood(int* features)
{
	this->feature = new int[NEIGHBORHOODSIZE1];
	for (int i = 0; i != NEIGHBORHOODSIZE1; i++)
		this->feature[i] = features[i];
	this->updateHash();
}

Neighborhood::Neighborhood(const Neighborhood& other)
{
	this->feature = new int[NEIGHBORHOODSIZE1];
	for (int i = 0; i != NEIGHBORHOODSIZE1; i++)
		this->feature[i] = other.feature[i];
	this->hash = other.hash;
}

Neighborhood::Neighborhood(void): feature(NULL)
{
	//this->feature = new int[NEIGHBORHOODSIZE1];
	//for (int i = 0; i != NEIGHBORHOODSIZE1; i++)
	//	this->feature[i] = 0;
	//this->hash = 0;
}

Neighborhood::Neighborhood(const ImageMatrix& image, int row, int col)
{
	this->feature = new int[NEIGHBORHOODSIZE1];
	this->updateFromImageMatrix(image, row, col);
}

Neighborhood::~Neighborhood(void)
{
	if (feature != NULL)
		delete[] feature;
}

Neighborhood& Neighborhood::operator= (const Neighborhood& other)
{
	if (&other == this)
		return *this;
	if (this->feature != NULL)
		delete[] feature;
	feature = new int[NEIGHBORHOODSIZE1];
	for (int i = 0; i < NEIGHBORHOODSIZE1; i++)
		this->feature[i] = other.feature[i];
	this->hash = other.hash;
	return *this;
}

int Neighborhood::getSize() const
{
	return NEIGHBORHOODSIZE1;
}

std::ostream& operator<<(std::ostream& stream, const Neighborhood& nb)
{
	int i = 0;
	try
	{
		while(nb.feature[i] < 3 && nb.feature[i] > -3)
			stream << nb.feature[i++] << ",";
	}

	catch (exception e)
	{
		return stream;
	}
	return stream;
}

std::ostream& toStream (std::ostream& stream, const Neighborhood& nb, int nbSize)
{
	for (int i = 0; i < nbSize; ++i)
		stream << nb.feature[i] << ",";
	return stream;
}
